package com.course.oauth2.config;

import com.course.common.model.domain.SignInIdentity;
import com.course.oauth2.service.UserService;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.Resource;
import java.util.LinkedHashMap;

/**
 * @PROJECT_NAME: course
 * @DESCRIPTION: 授权服务
 * @Author: 涂玄武
 * @DATE: 2021/6/18 15:30
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    /**
     * RedisTokenStore
     */
    @Resource
    private RedisTokenStore redisTokenStore;

    /**
     * 认证管理对象
     */
    @Resource
    private AuthenticationManager authenticationManager;

    /**
     * 密码编码器
     */
    @Resource
    private PasswordEncoder passwordEncoder;

    /**
     * 客户端配置类
     */
    @Resource
    private ClientOAuth2DataConfiguration clientOAuth2DataConfiguration;

    /**
     * 登录校验
     */
    @Resource
    private UserService userService;

    /**
     * 配置令牌端点安全约束
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许访问token的公钥，默认/oauth/token_key是受保护的
        security.tokenKeyAccess("permitAll()")
                //允许检查token的状态，默认/oauth/check_token是受保护的
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端配置 - 授权模型 - 此处用的password模型
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient(clientOAuth2DataConfiguration.getClientId())  //客户端标识ID
                .secret(passwordEncoder.encode(clientOAuth2DataConfiguration.getSecret()))  //客户端安全码
                .authorizedGrantTypes(clientOAuth2DataConfiguration.getGrantTypes())    //授权类型
                .accessTokenValiditySeconds(clientOAuth2DataConfiguration.getTokenValidityTime())   //token有效期
                .refreshTokenValiditySeconds(clientOAuth2DataConfiguration.getRefreshTokenValidtyTime())    //refresh-token有效期
                .scopes(clientOAuth2DataConfiguration.getScopes());  //客户端访问范围
    }

    /**
     * 配置授权以及令牌的访问端点和令牌服务
     * 使用redis存储token会使资源服务器频繁访问授权服务器
     * 优化点：
     *  使用Jwt来做token的存储，让资源服务器不再访问授权服务器
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //认证器
        endpoints.authenticationManager(authenticationManager)
                //具体登录的方法
                .userDetailsService(userService)
                //token存储方式:redis
                .tokenStore(redisTokenStore)
                //token存储方式:jwt
//                .tokenStore(jwtTokenStore())
//                .tokenEnhancer(jwtAccessTokenConverter())
                //令牌增强对象，增强返回结果
                .tokenEnhancer((accessToken, authentication) -> {
                    //获取登录后用户的信息，然后设置
                    SignInIdentity signInIdentity = (SignInIdentity)authentication.getPrincipal();
                    LinkedHashMap<String, Object> map = new LinkedHashMap<>();
                    map.put("nickname",signInIdentity.getNickname());
                    map.put("avatarUrl",signInIdentity.getAvatarUrl());
                    DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
                    token.setAdditionalInformation(map);
                    return token;
                });
    }

//    private TokenStore jwtTokenStore(){
//        JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
//        return jwtTokenStore;
//    }
//
//    private JwtAccessTokenConverter jwtAccessTokenConverter(){
//        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//        //读取classpath下的秘钥文件
//        ClassPathResource classPathResource = new ClassPathResource("coinexchange.jks");
//        //获取KeyStoreKeyFactory
//        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource,"coinexchange".toCharArray());
//        //给JwtAccessTokenConverter设置一个密钥对
//        jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("coinexchange","coinexchange".toCharArray()));
//
//        return jwtAccessTokenConverter;
//    }

}
